﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.ServiceModel;
using System.Text.RegularExpressions;
using Dqaqs.Data;
using Dqaqs.Services.ServiceContracts;

namespace Dqaqs.Services
{
	public class ProjectService : IProjectService
	{
		public void NewProject(Project project)
		{
			if (CheckProjectExists(project.Id, project.Title))
			{
				throw new InvalidOperationException("Similar project already exists.");
			}
			using (var d = DataConatiner.New())
			{
				project.CreatedAt = DateTime.Now;
				d.Projects.AddObject(project);
				d.SaveChanges();
			}
		}

		public Project GetProject(Guid id)
		{
			using (var d = DataConatiner.New())
			{
				return d.Projects.FirstOrDefault(p => p.Id.Equals(id));
			}
		}

		public IEnumerable<Project> ListProjects()
		{
			try
			{
				using (var d = DataConatiner.New())
				{
					return d.Projects.ToList();
				}
			}
			catch (Exception ex)
			{
				throw new FaultException(new FaultReason(ex.ToString()));
			}
		}

		public void UpdateProject(Project project)
		{
			using (var d = DataConatiner.New())
			{
				var prj = d.Projects.FirstOrDefault(p => p.Id.Equals(project.Id));
				if (prj == null)
					throw new InvalidOperationException("Project does not exist");
				d.ApplyCurrentValues("Projects", project);
				d.SaveChanges();
			}
		}

		public bool CheckProjectExists(Guid id, string title)
		{
			using(var d = DataConatiner.New())
			{
				if (d.Projects.Any( p => p.Id == id || p.Title == title))
				{
					return true;
				}
			}
			return false;
		}

		public string ValidateSchema(string schema)
		{
			try
			{
				SchemaFromString(schema);
			}
			catch (SyntaxErrorException ex)
			{
				return ex.Message;
			}
			return String.Empty;
		}

		public string ProcessSchema(string schema)
		{
			return SchemaFromString(schema).ToString();
		}

		private static Schema SchemaFromString(string schema)
		{
			var se = new Schema();
			var tables = new List<SchemaTable>();
			var supportedTypes = new[] { "string", "integer", "datetime", "number" };
			var tableRegex = new Regex(@"^[\+\s]+(\w+)(.*)");
			var columnRegex = new Regex(@"[\-\s]+(\*?\w+)\s*\(\s*(\w+)\s*\)(.*)");
			//Quick and dirty schema validation. Not ideal though.
			var lines = schema.Split(new[] { '\r', '\n' });
			var table = default(SchemaTable);
			var columns = new List<SchemaColumn>();
			for (var i = 0; i < lines.Length; i++)
			{
				//Line can be table, column, or empty.
				if (String.IsNullOrWhiteSpace(lines[i]))
					continue;
				var tblMatch = tableRegex.Match(lines[i]);
				if (tblMatch.Success)
				{
					if (tblMatch.Groups.Count > 3 ||
					    (tblMatch.Groups.Count > 1 && !String.IsNullOrWhiteSpace(tblMatch.Groups[2].Value)))
						ThrowSchemaSyntaxError(i, lines[i], tblMatch.Groups[1].Value.Replace(" ", "").First());
					if (table != null)
					{
						table.Columns = columns.ToArray();
						tables.Add(table);
					}
					table = new SchemaTable {Name = tblMatch.Groups[1].Value};
					columns = new List<SchemaColumn>();
					continue;
				}

				var colMatch = columnRegex.Match(lines[i]);
				if (colMatch.Success)
				{
					if (tblMatch.Groups.Count > 4 ||
					    (colMatch.Groups.Count > 2 && !string.IsNullOrWhiteSpace(tblMatch.Groups[3].Value)))
						ThrowSchemaSyntaxError(i, lines[i], colMatch.Groups[2].Value.Replace(" ", "").First());
					if (table == null)
						ThrowSchemaSyntaxError(i, lines[i], '-', "Table definition was expected.");
					var colName = colMatch.Groups[1].Value;
					var colType = colMatch.Groups[2].Value;
					if (!supportedTypes.Contains(colType))
						ThrowSchemaSyntaxError(i, lines[i], '-', String.Format("Type {0} is incorrect or unsupported.", colType));
					columns.Add(new SchemaColumn
					            	{
					            		Name = colName.StartsWith("*") ? colName.Remove(0,1) : colName,
					            		IsKey = colName.StartsWith("*"),
					            		Type = colType
					            	});
					continue;
				}
				ThrowSchemaSyntaxError(i, lines[i], '-', "Table or column expected.");
			}

			if (table != null)
			{//Wrap the last table
				table.Columns = columns.ToArray();
				tables.Add(table);
			}

			se.Tables = tables.ToArray();
			return se;
		}

		private static void ThrowSchemaSyntaxError(int i, string s, char unrec, string message = null)
		{
			throw new SyntaxErrorException(
				String.Format("Error parsing schema at line {0} : {1}. {2}",
							  i, s, message ?? String.Format("Unexpeted charachter {0}", unrec))
				);
		}

		public void DeleteProject(Guid id)
		{
			using (var d = DataConatiner.New())
			{
				var prj = d.Projects.FirstOrDefault(p => p.Id.Equals(id));
				if (prj != null)
					d.DeleteObject(prj);
				d.SaveChanges();
			}
		}
	}
}
